#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include"Log.hpp"
#include <memory>
#include"Session.hpp"
using namespace std;
const static string http_sep = "\r\n";
const static string line_sep = ": ";
const static string prefix_path = "wwwroot";
const static string home_page = "index.html";
const static string http_version = "HTTP/1.0";
const static string space_sep = " ";
const static string suffix_sep = ".";
const static string page_404 = "wwwroot/404.html";
const static string get_sep = "?";

class HttpRequest
{
private:
    string GetLine(string &request)
    {
        auto pos = request.find(http_sep);
        if (pos == string::npos)
            return "";
        string line = request.substr(0, pos);
        request.erase(0, line.size() + http_sep.size());
        if (line.empty())
        {
            return http_sep;
        }
        return line;
    }
    void ParseReqLine()
    {
        stringstream ss(_req_line);
        ss >> _method >> _url >> _version;
        if (_method == "GET")
        {
            auto pos = _url.find(get_sep);
            if (pos != string::npos)
            {
                _args = _url.substr(pos + get_sep.size());
                _url.erase(pos, string::npos);
            }
        }
        _path += _url;
        if (_path[_path.size() - 1] == '/')
        {
            _path += home_page;
        }
        auto pos = _path.find(suffix_sep);
        if (pos != string::npos)
        {
            _suffix = _path.substr(pos);
        }
        else
        {
            _suffix = ".default";
        }
    }
    void ParseReqHeader()
    {
        string prefix = "Cookie";
        string prefix2 = "sessionid=";
        for (auto &i : _req_headers)
        {
            auto pos = i.find(line_sep);
            if (pos == string::npos)
                continue;
            string key = i.substr(0, pos);
            string value = i.substr(pos + line_sep.size());
            if (key.empty() || value.empty())
                continue;
            _headers_kv.insert(make_pair(key, value));
            if(strncmp(key.c_str(),prefix.c_str(),prefix.size())==0)
            {
                _cookies.push_back(value);
                    LOG(DEBUG, "%s\n", value.c_str());

                if(strncasecmp(value.c_str(),prefix2.c_str(),prefix2.size())==0)
                {
                    _sessionid = value.substr(prefix2.size());
                    LOG(DEBUG, "%s\n", _sessionid.c_str());
                }
            }
        }
    }

public:
    HttpRequest() : _blank_line(http_sep), _path(prefix_path) {}
    void Deserialize(string &request)
    {
        _req_line = GetLine(request);
        string header;
        do
        {
            header = GetLine(request);
            if (header.empty())
                break;
            if (header == http_sep)
                break;
            _req_headers.push_back(header);
        } while (1);
        if (!request.empty())
            _body_text = request;
        ParseReqLine();
        ParseReqHeader();
    }
    string Url()
    {
        LOG(DEBUG, "client want url %s\n", _url.c_str());
        return _url;
    }
    string Path()
    {
        LOG(DEBUG, "client want path %s\n", _path.c_str());
        return _path;
    }
    string Suffix()
    {
        return _suffix;
    }
    string Method()
    {
        return _method;
    }
    string Getargs()
    {
        LOG(DEBUG, "client request method:%s ,args:%s \n", _method.c_str(), _args.c_str());
        return _args;
    }
    string SessionId()
    {
        return _sessionid;
    }
    void Print()
    {
        cout << "###" << _req_line << endl;
        for (auto &i : _req_headers)
        {
            cout << "@@@" << i << endl;
        }
        cout << "***" << _blank_line;
        cout << ">>>" << _body_text << endl;
        cout << _url << endl;
        for (auto &i : _headers_kv)
        {
            cout << i.first << ' ' << i.second << endl;
        }
    }
    ~HttpRequest() {}

private:
    string _req_line;
    vector<string> _req_headers;
    string _blank_line;
    string _body_text;
    // 更具体的字段
    string _method;
    string _url;
    string _version;
    string _path;
    string _suffix; // 请求的资源后缀
    unordered_map<string, string> _headers_kv;
    string _args;
    vector<string> _cookies;
    string _sessionid="";
};
class HttpResponse
{
private:
    std::string GetMonthName(int month)
    {
        std::vector<std::string> months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
        return months[month];
    }
    std::string GetWeekDayName(int day)
    {
        std::vector<std::string> weekdays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        return weekdays[day];
    }
    string ExpireTimeUseRfc1123(int t)
    {
        time_t timeout = time(nullptr) + t;
        struct tm *tm = gmtime(&timeout);
        char timebuf[1024];
        snprintf(timebuf, sizeof(timebuf), "%s, %02d %s %d %02d:%02d:%02d UTC",
                 GetWeekDayName(tm->tm_wday).c_str(),
                 tm->tm_mday,
                 GetMonthName(tm->tm_mon).c_str(),
                 tm->tm_year+1900,
                 tm->tm_hour,
                 tm->tm_min,
                 tm->tm_sec);
              // LOG(DEBUG, "error\n");
        return timebuf;
    }

public:
    HttpResponse() : _version(http_version), _blank_line(http_sep) {}
    void AddCode(int code, const string &desc)
    {
        _status_code = code;
        _desc = desc;
    }
    void AddHeader(const string &k, const string &v)
    {
        _heads_kv[k] = v;
    }
    void AddBodyTest(const string &body_test)
    {
        _body_text = body_test;
    }
    void AddCookieTimeOut()
    {
        string s = "username=syx; expires=" + ExpireTimeUseRfc1123(60) + ';';
        _heads_kv["Set-Cookie"] = s;
    }
    void AddSession(const string &sessionid)
    {
        string s = "sessionid=" + sessionid + ';' + " expires=" + ExpireTimeUseRfc1123(120) + ';';
        _heads_kv["Set-Cookie"] = s;
    }
    string Seserialize()
    {
        // status line
        _status_line = _version + space_sep + to_string(_status_code) + space_sep + _desc + http_sep;

        // headers
        for (auto &i : _heads_kv)
        {
            string head_line = i.first + line_sep + i.second + http_sep;
            //LOG(DEBUG, "add %s", head_line.c_str());
            _resp_headers.push_back(head_line);
        }
        // 空行和正文属性中有
        // 整合
        string respstr = _status_line;
        for (auto &i : _resp_headers)
        {
            respstr += i;
        }
        respstr += _blank_line;
        respstr += _body_text;
        return respstr;
    }

private:
    // 基本属性
    string _status_line;
    vector<string> _resp_headers;
    string _blank_line;
    string _body_text;
    // 详细属性
    string _version;
    int _status_code;
    string _desc;
    unordered_map<string, string> _heads_kv;
};
class HttpServer
{
private:
    string GetFileContent(const string &path)
    {
        // 以二进制读取
        std::ifstream in(path, std::ios::binary);
        if (!in.is_open())
            return "";
        in.seekg(0, in.end);
        int size = in.tellg(); // 偏移量多少
        in.seekg(0, in.beg);
        std::unique_ptr<char[]> content(new char[size]);
        in.read(content.get(), size);
        in.close();
        return content.get();
    }

public:
    HttpServer()
    {
        _mine_type[".html"] = "text/html";
        _mine_type[".jpg"] = "image/jpeg";
        _mine_type[".png"] = "image/png";
        _mine_type[".default"] = "text/html";
        _code_to_desc[200] = "OK";
        _code_to_desc[404] = "NOT FIND";
        _code_to_desc[301] = "Moved Permanently";
        _code_to_desc[302] = "Found";
        _session_manager = make_shared<SessionManager>();
    }
    string HandlerHttpRequest(string &request)
    {
#ifdef TEST
        cout << "----------------------------------------" << endl;
        cout << request << endl;
        string response = "HTTP/1.1 200 OK\r\n";
        response += "Content-Type: text/html\r\n";
        response += "\r\n";
        response += "<html><h1>syx,666</h1></html>";
        return response;
#else
        HttpRequest req;
        HttpResponse resp;
        req.Deserialize(request);
        //req.Print();
        req.Getargs();
        if (req.Path() == "wwwroot/redir.html")
        {
            resp.AddCode(302, _code_to_desc[302]);
            resp.AddHeader("Location", "https://www.qq.com");
        }
        else
        {
            string content = GetFileContent(req.Path());
            if (content.empty())
            {
                content = GetFileContent(page_404);
                resp.AddCode(404, _code_to_desc[404]);
            }
            else
                resp.AddCode(200, _code_to_desc[200]);
            //判断session有没有，没有就添加，有的话打印名字
            string sessionid = req.SessionId();
            static int number = 0;
            if (sessionid == "")
            {
                string name = "syx " + to_string(number++);
                session_ptr session = make_shared<Session>(name, "logined");
                sessionid=_session_manager->AddSession(session);
                LOG(INFO, "new session,add name: %s sessionid: %s\n",name.c_str(),sessionid.c_str());
                resp.AddSession(sessionid);
            }
            else{
                session_ptr session = _session_manager->GetSession(sessionid);
                if(session!=nullptr)
                {
                    LOG(DEBUG,"%s 正在活跃\n",session->_username.c_str());
                }
                else
                {
                    LOG(DEBUG,"%s 已过期，需要清理\n",sessionid.c_str());

                }
            }
            resp.AddHeader("Content-Length", to_string(content.size())); // body的长度
            resp.AddHeader("Content-Type", _mine_type[req.Suffix()]);  // 类型
            //resp.AddCookieTimeOut(); // Cookie
            resp.AddBodyTest(content);
        } // 实际上这里可以多加点，比如处理参数
        return resp.Seserialize();

#endif
    }
    ~HttpServer() {}

private:
    unordered_map<string, string> _mine_type;
    unordered_map<int, string> _code_to_desc;
    shared_ptr<SessionManager> _session_manager;
};